#include<SuperOF.h>
using namespace std;

const string version = "1.0.04.025";
int for_time = 0, errors = 0, warnings = 0;
map<char, bool>keydown;
string OFK_file, key;

struct Program
{
	string code[4095];
	bool show_info = true;
	bool show_warning = true;
	bool show_error = true;
	bool debug_mode = false;
	bool ignore_error = false;
	bool program_check = false;
	const int num = 6;
	int length;
};
Program main_program;

int Execute_single_line_code(int for_deep, int for_time,int code_num)
{
	if (main_program.debug_mode)
		Sleep(1000);
	for_time++;
	bool flag = true;
	int oper_len;
	char operation_c[128] = { "" };
	string operation = "", line_code = "";
	getline(cin, line_code);
	if (line_code == "END" || line_code == "END;")
		return 1;
	if (line_code == "EDFF" || line_code == "EDFF;")
	{
		if (for_deep <= 0)
		{
			error("/???????FF?EDFF");
			return 1;
		}
		if (for_time <= 0)
		{
			info("/????");
		}
	}
	for (oper_len = 0; oper_len < line_code.length(); oper_len++)
	{
		if (line_code[oper_len] == ' ' || line_code[oper_len] == ';')
		{
			flag = false;
			break;
		}
	}
	if (flag)
	{
		errors += 1;
		warnings += 1;
		if (main_program.show_error)
			error("Cannot find operation code in line " + int_to_string(for_time) + ".");
		if (main_program.show_warning)
			warning("Don't forget to add \" \" or \";\"?");
		if (main_program.program_check || main_program.ignore_error)
			return 0;
		if (main_program.show_warning)
			warning("Program will close in 5 seconds.");
		Sleep(5000);
		return 1;
	}
	if (oper_len >= 127)
	{
		errors += 1;
		if (main_program.show_error)
			error("Operation code is too long in line " + int_to_string(for_time) + ".Expect less than 127,read" + int_to_string(oper_len) + ".");
		if (main_program.program_check || main_program.ignore_error)
			return 0;
		if (main_program.show_warning)
			warning("Program will close in 5 seconds.");
		Sleep(5000);
		return 1;
	}
	strncpy(operation_c, line_code.c_str(), oper_len);
	operation = operation_c;
	if (main_program.show_info)
		info("operation:" + operation + ".");
	if (operation == "BEGIN")
	{
		if (main_program.show_info)
			info("Program start.");
		if (line_code.length() > 6)
		{
			if (main_program.show_info)
				info("Parsing parameters...");
			string x = line_code;
			x.erase(0, oper_len + 1);
			if (x.length() != main_program.num)
			{
				errors += 1;
				if (main_program.show_error)
					error("BEGIN must has 0 or " + int_to_string(main_program.num) + " bool parameters.");
				if (main_program.show_warning)
					warning("Parameters in BEGIN will be invalid.");
				return 0;
			}
			main_program.show_info = x[0] - '0';
			main_program.show_warning = x[1] - '0';
			main_program.show_error = x[2] - '0';
			main_program.debug_mode = x[3] - '0';
			main_program.ignore_error = x[4] - '0';
			main_program.program_check = x[5] - '0';
		}
		return 0;
	}
	if (operation == "KD")
	{
		if (main_program.show_info)
			info("Read key down in line " + int_to_string(for_time) + ".");
		string x = line_code;
		x.erase(0, oper_len + 1);
		if (x.size() != 3 || x[0] != '\'' || x[2] != '\'')
		{
			errors += 1;
			if (main_program.show_error)
				error("KD in line " + int_to_string(for_time) + " needs to add a ASCII character like 'A'.");
			if (main_program.program_check || main_program.ignore_error)
				return 0;
			if (main_program.show_warning)
				warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 1;
		}
		if (main_program.show_info)
			info("Key \'" + char_to_string(x[1]) + "\' was down.");
		if (!main_program.program_check)
			keybd_event(x[1], 0, 0, 0);
		key += char_to_string(x[1]);
		keydown[x[1]] = true;
		return 0;
	}
	if (operation == "KU")
	{
		if (main_program.show_info)
			info("Read key up in line " + int_to_string(for_time) + ".");
		string x = line_code;
		x.erase(0, oper_len + 1);
		if (x.size() != 3 || x[0] != '\'' || x[2] != '\'')
		{
			errors += 1;
			if (main_program.show_error)
				error("KU in line " + int_to_string(for_time) + " needs to add a ASCII character like 'A'.");
			if (main_program.program_check || main_program.ignore_error)
				return 0;
			if (main_program.show_warning)
				warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 1;
		}
		if (main_program.show_info)
			info("Key \'" + char_to_string(x[1]) + "\' was up.");
		if (!main_program.program_check)
			keybd_event(x[1], 0, 2, 0);
		keydown[x[1]] = false;
		return 0;
	}
	if (operation == "KDU")
	{
		if (main_program.show_info)
			info("Read key down and up in line " + int_to_string(for_time) + ".");
		string x = line_code;
		x.erase(0, oper_len + 1);
		int kdu_s = 0;
		for (int i = 0; i < x.length(); i++)
		{
			if (!main_program.program_check)
				Sleep(kdu_s);
			if (x[i] == '(')
			{
				kdu_s = 0;
				i++;
				while (x[i] != ')')
				{
					if (x[i] >= '0' || x[i] <= '9')
					{
						kdu_s *= 10;
						kdu_s += x[i] - '0';
					}
					else
					{
						errors += 1;
						if (main_program.show_error)
							error("KDU in line " + int_to_string(for_time) + " needs to add a number in \'( )\'.");
						if (main_program.program_check || main_program.ignore_error)
							return 0;
						if (main_program.show_warning)
							warning("Program will close in 5 seconds.");
						Sleep(5000);
						return 1;
					}
					i++;
					if (!(i < x.length()))
					{
						errors += 1;
						if (main_program.show_error)
							error("Cannot find corresponding bracket in line " + int_to_string(for_time) + ".");
						if (main_program.program_check || main_program.ignore_error)
						{
							kdu_s = 0;
							goto next_line;
						}
						if (main_program.show_warning)
							warning("Program will close in 5 seconds.");
						Sleep(5000);
						return 1;
					}
				}
				if (main_program.show_info)
					info("KDU in line " + int_to_string(for_time) + "\'s waiting time set " + int_to_string(kdu_s) + " ms.");
				if (kdu_s < 0)
				{
					warnings += 1;
					if (main_program.show_warning)
						warning("KDU in line " + int_to_string(for_time) + "\'s waiting should be more than 0 ms.");
				}
				i++;
			}
			if (x[i] == ' ')
			{
				if (main_program.show_info)
					info("Key ' ' was down and up.");
				if (!main_program.program_check)
				{
					keybd_event(0x20, 0, 0, 0);
					keybd_event(0x20, 0, 2, 0);
				}
				keydown[' '] = false;
				return 0;
			}
			if (!((x[i] >= 'A' && x[i] <= 'Z') || (x[i] >= '0' && x[i] <= '9') || x[i] == '+' || x[i] == '-' || x[i] == ',' || x[i] == '.'))
			{
				errors += 1;
				if (main_program.show_error)
					error("KDU in line " + int_to_string(for_time) + " needs to add A-Z,0-9 or '-' '+' ',' '.' ' ' like \"hello world.\".");
				if (main_program.program_check || main_program.ignore_error)
					return 0;
				if (main_program.show_warning)
					warning("Program will close in 5 seconds.");
				Sleep(5000);
				return 1;
			}
			if (x[i] == '+' || x[i] == '-' || x[i] == ',' || x[i] == '.')
			{
				map<char, int> amcp;
				amcp['+'] = VK_OEM_PLUS;
				amcp['-'] = VK_OEM_MINUS;
				amcp[','] = VK_OEM_COMMA;
				amcp['.'] = VK_OEM_PERIOD;
				if (main_program.show_info)
					info("Key \'" + char_to_string(x[i]) + "\' was down and up.");
				if (!main_program.program_check)
				{
					keybd_event(amcp[x[i]], 0, 0, 0);
					keybd_event(amcp[x[i]], 0, 2, 0);
				}
				keydown[x[i]] = false;
				return 0;
			}
			else
			{
				if (main_program.show_info)
					info("Key \'" + char_to_string(x[i]) + "\' was down and up.");
				if (!main_program.program_check)
				{
					keybd_event(x[i], 0, 0, 0);
					keybd_event(x[i], 0, 2, 0);
				}
				keydown[x[i]] = false;
				return 0;
			}
		}
	next_line:
		return 0;
	}
	if (operation == "W")
	{
		if (main_program.show_info)
			info("Read wait in line " + int_to_string(for_time) + ".");
		string x = line_code;
		int num = 0;
		x.erase(0, oper_len + 1);
		for (int i = 0; i < x.length(); i++)
		{
			num *= 10;
			if (x[i] >= '0' && x[i] <= '9')
				num += x[i] - '0';
			else
			{
				errors += 1;
				if (main_program.show_error)
					error("W in line " + int_to_string(for_time) + " needs to add a number like 3000.");
				if (main_program.program_check || main_program.ignore_error)
					return 0;
				if (main_program.show_warning)
					warning("Program will close in 5 seconds.");
				Sleep(5000);
				return 1;
			}
		}
		if (main_program.show_info)
			info("Start waiting for " + int_to_string(num) + "ms.");
		if (!main_program.program_check)
			Sleep(num);
		if (main_program.show_info)
			info("Finish waiting.");
		return 0;
	}
	if (operation == "P")
	{
		if (main_program.show_info)
			info("Read pause in line " + int_to_string(for_time) + ".");
		cout << "Press any key to return 0..." << endl;
		if (!main_program.program_check)
			getch();
		return 0;
	}
	if (operation == "FF")
	{
		string x = line_code;
		int num = 0;
		x.erase(0, oper_len + 1);
		for (int i = 0; i < x.length(); i++)
		{
			num *= 10;
			if (x[i] >= '0' && x[i] <= '9')
				num += x[i] - '0';
			else
			{
				error("?????");
			}
		}
		while ()/////////////////////////////////////////////////////////////////////////////////////////////////////////
	}
	errors += 1;
	if (main_program.show_error)
		error("Can't find right operation code in line " + int_to_string(for_time) + ".");
	if (main_program.program_check)
		return 0;
	if (main_program.show_warning)
		warning("Program will close in 5 seconds.");
	Sleep(5000);
	return 1;
}

int main()
{
	main_program.program_check = true;
	color("cyan-l", "black", 0, 0);
	cout << "OF-keyboard V" << version << endl;
	cout << "if-OF(c)2020-2021" << endl;
	color("white", "black", 0, 0);
	line();
	cout << "Please type the file directory:" << endl;
cin_file_dir:
	getline(cin, OFK_file);
	if (strstr(OFK_file.c_str(), ":") == NULL)
	{
		OFK_file = work_dir() + "\\" + OFK_file;
		fstream _file;
		_file.open(OFK_file.c_str(), ios::in);
		if (!_file)
		{
			error("Can't find the file.");
			warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
	}
	else
	{
		fstream _file;
		_file.open(OFK_file.c_str(), ios::in);
		if (!_file)
		{
			error("Can't find the file.");
			warning("Program will close in 5 seconds.");
			Sleep(5000);
			return 0;
		}
	}
	if (OFK_file[OFK_file.size() - 1] != 'K' && OFK_file[OFK_file.size() - 1] != 'k' && OFK_file[OFK_file.size() - 2] != 'F' && OFK_file[OFK_file.size() - 2] != 'f' && OFK_file[OFK_file.size() - 3] != 'O' && OFK_file[OFK_file.size() - 3] != 'o')
	{
		warning(OFK_file + " is not an OF-Keybord(ofk) file.Are you sure you type right file name?");
		if (MessageBox(NULL, (OFK_file + " is not an OF-Keybord(ofk) file.Are you sure you type right file name?").c_str(), "Warning", MB_YESNO + 48) == 7)
		{
			info("Please type the file directory again.");
			goto cin_file_dir;
		}
	}
	FILE* file = fopen(OFK_file.c_str(), "r");
	int size = filelength(fileno(file)), count;
	for (count = 0; fgetc(file) != EOF; count++);
	fclose(file);
	info("Loading program...");
	line();
	info("Code length:" + int_to_string(count) + ".");
	info("Code size:" + int_to_string(size) + "B.");
	freopen(OFK_file.c_str(), "r", stdin);
	info("Program is running now...");
	line();
	do
	{
		getline(cin, program);
	} while ();
	while (1)
	{
		if (Execute_single_line_code(0, 1) == 1)
			break;
	}
	bool es = false;
	for (int i = 0; i < key.length(); i++)
	{
		if (keydown[key[i]])
		{
			warnings += 1;
			if (main_program.show_warning)
				warning("Key '" + char_to_string(key[i]) + "' was still down.");
			es = true;
		}
	}
	if (es)	wait(1);
	if (main_program.debug_mode)
	{
		cout << "show_info:\t" << main_program.show_info << endl;
		cout << "show_warning:\t" << main_program.show_warning << endl;
		cout << "show_error:\t" << main_program.show_error << endl;
		cout << "debug_mode:\t" << main_program.debug_mode << endl;
		cout << "ignore_error:\t" << main_program.ignore_error << endl;
		cout << "program_check:\t" << main_program.program_check << endl;
	}
	if (main_program.program_check || main_program.debug_mode)
	{
		cout << endl;
		line();
		info("Finish program check.");
		cout << "Errors:\t\t" << errors << endl;
		cout << "Warnings:\t" << warnings << endl;
		cout << "Press any key to continue..." << endl;
		getch();
	}
	return 0;
}
